﻿/*	Copyright (c) 2018 Jean-Marc VIGLINO, 
	released under the CeCILL-B license (French BSD license)
	(http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/

import {DEVICE_PIXEL_RATIO as ol_has_DEVICE_PIXEL_RATIO} from 'ol/has.js'
import ol_style_Stroke from 'ol/style/Stroke.js'
import {asString as ol_color_asString} from 'ol/color.js'
import ol_style_FillPattern from './FillPattern.js'

/**
 * @classdesc
 * Stroke style with named pattern
 *
 * @constructor
 * @param {any}  options
 *	@param {ol.style.Image|undefined} options.image an image pattern, image must be preloaded to draw on first call
 *	@param {number|undefined} options.opacity opacity with image pattern, default:1
 *	@param {string} options.pattern pattern name (override by image option)
 *	@param {ol.colorLike} options.color pattern color
 *	@param {ol.style.Fill} options.fill fill color (background)
 *	@param {number|Array<number>} options.offset pattern offset for hash/dot/circle/cross pattern
 *	@param {number} options.size line size for hash/dot/circle/cross pattern
 *	@param {number} options.spacing spacing for hash/dot/circle/cross pattern
 *	@param {number|bool} options.angle angle for hash pattern / true for 45deg dot/circle/cross
 *	@param {number} options.scale pattern scale 
 * @extends {ol.style.Fill}
 * @implements {ol.structs.IHasChecksum}
 * @api
 */
var ol_style_StrokePattern = class olstyleStrokePattern extends ol_style_Stroke {
	constructor(options) {
		super(options)

		options = options || {}

		var pattern, i;
		var canvas = this.canvas_ = document.createElement('canvas')
		var scale = Number(options.scale) > 0 ? Number(options.scale) : 1
		var ratio = scale * ol_has_DEVICE_PIXEL_RATIO || ol_has_DEVICE_PIXEL_RATIO

		var ctx = canvas.getContext('2d')

		if (options.image) {
			options.image.load()

			var img = options.image.getImage()
			if (img.width) {
				canvas.width = Math.round(img.width * ratio)
				canvas.height = Math.round(img.height * ratio)
				ctx.globalAlpha = typeof (options.opacity) == 'number' ? options.opacity : 1
				ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
				pattern = ctx.createPattern(canvas, 'repeat')
			} else {
				var self = this
				pattern = [0, 0, 0, 0]
				img.onload = function () {
					canvas.width = Math.round(img.width * ratio)
					canvas.height = Math.round(img.height * ratio)
					ctx.globalAlpha = typeof (options.opacity) == 'number' ? options.opacity : 1
					ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
					pattern = ctx.createPattern(canvas, 'repeat')
					self.setColor(pattern)
				}
			}
		} else {
			var pat = this.getPattern_(options)
			canvas.width = Math.round(pat.width * ratio)
			canvas.height = Math.round(pat.height * ratio)
			ctx.beginPath()
			if (options.fill) {
				ctx.fillStyle = ol_color_asString(options.fill.getColor())
				ctx.fillRect(0, 0, canvas.width, canvas.height)
			}
			ctx.scale(ratio, ratio)
			ctx.lineCap = "round"
			ctx.lineWidth = pat.stroke || 1

			ctx.fillStyle = ol_color_asString(options.color || "#000")
			ctx.strokeStyle = ol_color_asString(options.color || "#000")
			if (pat.circles)
				for (i = 0; i < pat.circles.length; i++) {
					var ci = pat.circles[i]
					ctx.beginPath()
					ctx.arc(ci[0], ci[1], ci[2], 0, 2 * Math.PI)
					if (pat.fill)
						ctx.fill()
					if (pat.stroke)
						ctx.stroke()
				}

			if (!pat.repeat)
				pat.repeat = [[0, 0]]

			if (pat.char) {
				ctx.font = pat.font || (pat.width) + "px Arial"
				ctx.textAlign = 'center'
				ctx.textBaseline = 'middle'
				if (pat.angle) {
					ctx.fillText(pat.char, pat.width / 4, pat.height / 4)
					ctx.fillText(pat.char, 5 * pat.width / 4, 5 * pat.height / 4)
					ctx.fillText(pat.char, pat.width / 4, 5 * pat.height / 4)
					ctx.fillText(pat.char, 5 * pat.width / 4, pat.height / 4)

					ctx.fillText(pat.char, 3 * pat.width / 4, 3 * pat.height / 4)
					ctx.fillText(pat.char, -pat.width / 4, -pat.height / 4)
					ctx.fillText(pat.char, 3 * pat.width / 4, -pat.height / 4)
					ctx.fillText(pat.char, -pat.width / 4, 3 * pat.height / 4)
				}
				else
					ctx.fillText(pat.char, pat.width / 2, pat.height / 2)
			}

			if (pat.lines)
				for (i = 0; i < pat.lines.length; i++)
					for (var r = 0; r < pat.repeat.length; r++) {
						var li = pat.lines[i]
						ctx.beginPath()
						ctx.moveTo(li[0] + pat.repeat[r][0], li[1] + pat.repeat[r][1])
						for (var k = 2; k < li.length; k += 2) {
							ctx.lineTo(li[k] + pat.repeat[r][0], li[k + 1] + pat.repeat[r][1])
						}
						if (pat.fill)
							ctx.fill()
						if (pat.stroke)
							ctx.stroke()
						ctx.save()
						ctx.strokeStyle = 'red'
						ctx.strokeWidth = 0.1
						//ctx.strokeRect(0,0,canvas.width,canvas.height);
						ctx.restore()
					}
			pattern = ctx.createPattern(canvas, 'repeat')
			if (options.offset) {
				var offset = options.offset
				if (typeof (offset) == "number")
					offset = [offset, offset]
				if (offset instanceof Array) {
					var dx = Math.round((offset[0] * ratio))
					var dy = Math.round((offset[1] * ratio))
					// New pattern
					ctx.scale(1 / ratio, 1 / ratio)
					ctx.clearRect(0, 0, canvas.width, canvas.height)
					ctx.translate(dx, dy)
					ctx.fillStyle = pattern
					ctx.fillRect(-dx, -dy, canvas.width, canvas.height)
					pattern = ctx.createPattern(canvas, 'repeat')
				}
			}
		}

		this.setColor (pattern);
	}
	/**
	 * Clones the style.
	 * @return {ol_style_StrokePattern}
	 */
	clone() {
		var s = super.clone()
		s.canvas_ = this.canvas_
		return s
	}
	/** Get canvas used as pattern
	*	@return {canvas}
	*/
	getImage() {
		return this.canvas_
	}
	/** Get pattern
	*	@param {olx.style.FillPatternOption}
	*/
	getPattern_(options) {
		var pat = ol_style_FillPattern.patterns[options.pattern]
			|| ol_style_FillPattern.patterns.dot
		var d = Math.round(options.spacing) || 10
		var size
		//	var d2 = Math.round(d/2)+0.5;
		switch (options.pattern) {
			case 'dot':
			case 'circle':
				{
					size = options.size === 0 ? 0 : options.size / 2 || 2
					if (!options.angle) {
						pat.width = pat.height = d
						pat.circles = [[d / 2, d / 2, size]]
						if (options.pattern == 'circle') {
							pat.circles = pat.circles.concat([
								[d / 2 + d, d / 2, size],
								[d / 2 - d, d / 2, size],
								[d / 2, d / 2 + d, size],
								[d / 2, d / 2 - d, size],
								[d / 2 + d, d / 2 + d, size],
								[d / 2 + d, d / 2 - d, size],
								[d / 2 - d, d / 2 + d, size],
								[d / 2 - d, d / 2 - d, size]
							])
						}
					}

					else {
						d = pat.width = pat.height = Math.round(d * 1.4)
						pat.circles = [[d / 4, d / 4, size], [3 * d / 4, 3 * d / 4, size]]
						if (options.pattern == 'circle') {
							pat.circles = pat.circles.concat([
								[d / 4 + d, d / 4, size],
								[d / 4, d / 4 + d, size],
								[3 * d / 4 - d, 3 * d / 4, size],
								[3 * d / 4, 3 * d / 4 - d, size],
								[d / 4 + d, d / 4 + d, size],
								[3 * d / 4 - d, 3 * d / 4 - d, size]
							])
						}
					}
					break
				}
			case 'tile':
			case 'square':
				{
					size = options.size === 0 ? 0 : options.size / 2 || 2
					if (!options.angle) {
						pat.width = pat.height = d
						pat.lines = [[d / 2 - size, d / 2 - size, d / 2 + size, d / 2 - size, d / 2 + size, d / 2 + size, d / 2 - size, d / 2 + size, d / 2 - size, d / 2 - size]]
					}

					else {
						pat.width = pat.height = d
						//size *= Math.sqrt(2);
						pat.lines = [[d / 2 - size, d / 2, d / 2, d / 2 - size, d / 2 + size, d / 2, d / 2, d / 2 + size, d / 2 - size, d / 2]]
					}
					if (options.pattern == 'square')
						pat.repeat = [[0, 0], [0, d], [d, 0], [0, -d], [-d, 0], [-d, -d], [d, d], [-d, d], [d, -d]]
					break
				}
			case 'cross':
				{ // Limit angle to 0 | 45
					if (options.angle)
						options.angle = 45
				}
			// fallthrough
			case 'hatch':
				{
					var a = Math.round(((options.angle || 0) - 90) % 360)
					if (a > 180)
						a -= 360
					a *= Math.PI / 180
					var cos = Math.cos(a)
					var sin = Math.sin(a)
					if (Math.abs(sin) < 0.0001) {
						pat.width = pat.height = d
						pat.lines = [[0, 0.5, d, 0.5]]
						pat.repeat = [[0, 0], [0, d]]
					}
					else if (Math.abs(cos) < 0.0001) {
						pat.width = pat.height = d
						pat.lines = [[0.5, 0, 0.5, d]]
						pat.repeat = [[0, 0], [d, 0]]
						if (options.pattern == 'cross') {
							pat.lines.push([0, 0.5, d, 0.5])
							pat.repeat.push([0, d])
						}
					}

					else {
						var w = pat.width = Math.round(Math.abs(d / sin)) || 1
						var h = pat.height = Math.round(Math.abs(d / cos)) || 1
						if (options.pattern == 'cross') {
							pat.lines = [[-w, -h, 2 * w, 2 * h], [2 * w, -h, -w, 2 * h]]
							pat.repeat = [[0, 0]]
						}
						else if (cos * sin > 0) {
							pat.lines = [[-w, -h, 2 * w, 2 * h]]
							pat.repeat = [[0, 0], [w, 0], [0, h]]
						}

						else {
							pat.lines = [[2 * w, -h, -w, 2 * h]]
							pat.repeat = [[0, 0], [-w, 0], [0, h]]
						}

					}
					pat.stroke = options.size === 0 ? 0 : options.size || 4
					break
				}
			default: {
				break
			}
		}
		return pat
	}
}

export default ol_style_StrokePattern
